/* eslint-disable camelcase */
/* globals Creature,CreatureAnimation,CreatureManager,CreatureModuleUtils */
/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @author       Kestrel Moon Studios <creature@kestrelmoon.com>
 * @copyright    2016 Photon Storm Ltd and Kestrel Moon Studios
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

/**
 * @class CreatureShader
 * @constructor
 * @param gl {WebGLContext} the current WebGL drawing context
 */
PIXI.CreatureShader = function (gl)
{
    /**
     * @property _UID
     * @type Number
     * @private
     */
    this._UID = Phaser._UID++;

    /**
     * @property gl
     * @type WebGLContext
     */
    this.gl = gl;

    /**
     * The WebGL program.
     * @property program
     * @type Any
     */
    this.program = null;

    /**
     * The fragment shader.
     * @property fragmentSrc
     * @type Array
     */
    this.fragmentSrc = [
        '//CreatureShader Fragment Shader.',
        'precision mediump float;',
        'varying vec2 vTextureCoord;',
        'varying float vTextureIndex;',
        'varying vec4 vColor;',

        /*
         * 'uniform float alpha;',
         * 'uniform vec3 tint;',
         */
        'uniform sampler2D uSampler;',
        'void main(void) {',
        '   gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;',
        '}'
    ];

    /**
     * The vertex shader.
     * @property vertexSrc
     * @type Array
     */
    this.vertexSrc = [
        '//CreatureShader Vertex Shader.',
        'attribute vec2 aVertexPosition;',
        'attribute vec2 aTextureCoord;',
        'attribute float aTextureIndex;',
        'attribute vec4 aColor;',
        'uniform mat3 translationMatrix;',
        'uniform vec2 projectionVector;',
        'uniform vec2 offsetVector;',
        'uniform float alpha;',
        'uniform vec3 tint;',
        'varying vec2 vTextureCoord;',
        'varying float vTextureIndex;',
        'varying vec4 vColor;',

        'void main(void) {',
        '   vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);',
        '   v -= offsetVector.xyx;',
        '   gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);',
        '   vTextureCoord = aTextureCoord;',
        '   vTextureIndex = aTextureIndex;',
        '   vColor = vec4(tint[0], tint[1], tint[2], 1.0) * aColor.a * alpha;',
        '}'
    ];

    this.init();
};

PIXI.CreatureShader.prototype.constructor = PIXI.CreatureShader;

/**
 * Initialises the shader.
 *
 * @method PIXI.CreatureShader#init
 */
PIXI.CreatureShader.prototype.init = function ()
{
    var gl = this.gl;
    var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc);
    gl.useProgram(program);

    // get and store the uniforms for the shader
    this.uSampler = PIXI._enableMultiTextureToggle ?
        gl.getUniformLocation(program, 'uSamplerArray[0]') :
        gl.getUniformLocation(program, 'uSampler');

    this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
    this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
    this.colorAttribute = gl.getAttribLocation(program, 'aColor');
    this.aTextureIndex = gl.getAttribLocation(program, 'aTextureIndex');

    // this.dimensions = gl.getUniformLocation(this.program, 'dimensions');

    // get and store the attributes
    this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
    this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');

    this.attributes = [ this.aVertexPosition, this.aTextureCoord, this.colorAttribute ];

    this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix');
    this.alpha = gl.getUniformLocation(program, 'alpha');
    this.tintColor = gl.getUniformLocation(program, 'tint');

    this.program = program;
};

/**
 * Destroys the shader.
 *
 * @method PIXI.CreatureShader#destroy
 */
PIXI.CreatureShader.prototype.destroy = function ()
{
    this.gl.deleteProgram(this.program);
    this.uniforms = null;
    this.gl = null;

    this.attribute = null;
};

/**
 * Creature is a custom Game Object used in conjunction with the Creature Runtime libraries by Kestrel Moon Studios.
 *
 * It allows you to display animated Game Objects that were created with the [Creature Automated Animation Tool](http://www.kestrelmoon.com/creature/).
 *
 * Note 1: You can only use Phaser.Creature objects in WebGL enabled games. They do not work in Canvas mode games.
 *
 * Note 2: You must use a build of Phaser that includes the CreatureMeshBone.js runtime and gl-matrix.js, or have them
 * loaded before your Phaser game boots.
 *
 * See the Phaser custom build process for more details.
 *
 * By default the Creature runtimes are NOT included in any pre-configured version of Phaser.
 *
 * So you'll need to do `grunt custom` to create a build that includes them.
 *
 * @class Phaser.Creature
 * @extends PIXI.DisplayObjectContainer
 * @extends Phaser.Component.Core
 * @extends Phaser.Component.Angle
 * @extends Phaser.Component.AutoCull
 * @extends Phaser.Component.BringToTop
 * @extends Phaser.Component.Destroy
 * @extends Phaser.Component.FixedToCamera
 * @extends Phaser.Component.LifeSpan
 * @extends Phaser.Component.Reset
 * @extends Phaser.Component.InputEnabled
 * @constructor
 * @param {Phaser.Game} game - A reference to the currently running game.
 * @param {number} x - The x coordinate of the Game Object. The coordinate is relative to any parent container this Game Object may be in.
 * @param {number} y - The y coordinate of the Game Object. The coordinate is relative to any parent container this Game Object may be in.
 * @param {string|PIXI.Texture} key - The texture used by the Creature Object during rendering. It can be a string which is a reference to the Cache entry, or an instance of a PIXI.Texture.
 * @param {string} mesh - The mesh data for the Creature Object. It should be a string which is a reference to the Cache JSON entry.
 * @param {string} [animation='default'] - The animation within the mesh data  to play.
 * @param {string} [useFlatData=false] - Use flat data
 */
Phaser.Creature = function (game, x, y, key, mesh, animation, useFlatData)
{
    /**
     * @property {Phaser.Game} game - A reference to the currently running game.
     */
    this.game = game;

    if (animation === undefined) { animation = 'default'; }
    if (useFlatData === undefined) { useFlatData = false; }

    /**
     * @property {number} type - The const type of this object.
     * @readonly
     */
    this.type = Phaser.CREATURE;

    if (!game.cache.checkJSONKey(mesh))
    {
        console.warn('Phaser.Creature: Invalid mesh key given. Not found in Phaser.Cache');
        return;
    }

    var meshData = game.cache.getJSON(mesh, true);

    /**
     * @property {Creature} _creature - The Creature instance.
     * @private
     */
    this._creature = new Creature(meshData, useFlatData);

    /**
     * @property {CreatureAnimation} animation - The CreatureAnimation instance.
     */
    this.animation = new CreatureAnimation(meshData, animation, useFlatData);

    /**
     * @property {CreatureManager} manager - The CreatureManager instance for this object.
     */
    this.manager = new CreatureManager(this._creature);

    /**
     * @property {number} timeDelta - How quickly the animation advances.
     * @default
     */
    this.timeDelta = 0.05;

    if (typeof key === 'string')
    {
        var texture = new PIXI.Texture(game.cache.getBaseTexture(key));
    }
    else
    {
        var texture = key;
    }

    /**
     * @property {PIXI.Texture} texture - The texture the animation is using.
     */
    this.texture = texture;

    PIXI.DisplayObjectContainer.call(this);

    this.dirty = true;
    this.blendMode = PIXI.blendModes.NORMAL;

    /**
     * @property {Phaser.Point} creatureBoundsMin - The minimum bounds point.
     * @protected
     */
    this.creatureBoundsMin = new Phaser.Point();

    /**
     * @property {Phaser.Point} creatureBoundsMax - The maximum bounds point.
     * @protected
     */
    this.creatureBoundsMax = new Phaser.Point();

    var target = this.manager.target_creature;

    /**
     * @property {Float32Array} vertices - The vertices data.
     * @protected
     */
    this.vertices = new Float32Array(target.total_num_pts * 2);

    /**
     * @property {Float32Array} uvs - The UV data.
     * @protected
     */
    this.uvs = new Float32Array(target.total_num_pts * 2);

    /**
     * @property {Uint16Array} indices
     * @protected
     */
    this.indices = new Uint16Array(target.global_indices.length);

    for (var i = 0; i < this.indices.length; i++)
    {
        this.indices[i] = target.global_indices[i];
    }

    /**
     * @property {Uint16Array} colors - The vertices colors
     * @protected
     */
    this.colors = new Float32Array(target.total_num_pts * 4);
    for(var j = 0; j < this.colors.length; j++)
    {
        this.colors[j] = 1.0;
    }

    this.updateRenderData(target.global_pts, target.global_uvs);

    this.manager.AddAnimation(this.animation);
    this.manager.SetActiveAnimationName(animation, false);

    Phaser.Component.Core.init.call(this, game, x, y);

    /**
     * @property {number} tint - colour change
     * @default
     */
    this.data.tint = 0xFFFFFF;

    /**
     * @property {number} alpha - set the opacity
     * @default
     */
    this.data.alpha = 1.0;
};

Phaser.Creature.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
Phaser.Creature.prototype.constructor = Phaser.Creature;

Phaser.Component.Core.install.call(Phaser.Creature.prototype, [
    'Angle',
    'AutoCull',
    'BringToTop',
    'Destroy',
    'FixedToCamera',
    'LifeSpan',
    'Reset',
    'InputEnabled'
]);

Phaser.Creature.prototype.preUpdateInWorld = Phaser.Component.InWorld.preUpdate;
Phaser.Creature.prototype.preUpdateCore = Phaser.Component.Core.preUpdate;

/**
 * Automatically called by World.preUpdate.
 *
 * @method Phaser.Creature#preUpdate
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.preUpdate = function ()
{
    if (!this.preUpdateInWorld())
    {
        return false;
    }

    this.manager.Update(this.timeDelta);

    this.updateData();

    return this.preUpdateCore();
};

/**
 *
 *
 * @method Phaser.Creature#_initWebGL
 * @memberof Phaser.Creature
 * @private
 */
Phaser.Creature.prototype._initWebGL = function (renderSession)
{
    // build the strip!
    var gl = renderSession.gl;

    this._vertexBuffer = gl.createBuffer();
    this._indexBuffer = gl.createBuffer();
    this._uvBuffer = gl.createBuffer();
    this._colorBuffer = gl.createBuffer();

    gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);

    gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.DYNAMIC_DRAW);

    gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW);

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
};

/**
 * @method Phaser.Creature#_renderWebGL
 * @memberof Phaser.Creature
 * @private
 */
Phaser.Creature.prototype._renderWebGL = function (renderSession)
{
    //  If the sprite is not visible or the alpha is 0 then no need to render this element
    if (!this.visible || this.alpha <= 0)
    {
        return;
    }

    renderSession.spriteBatch.stop();

    // init! init!
    if (!this._vertexBuffer)
    {
        this._initWebGL(renderSession);
    }

    renderSession.shaderManager.setShader(renderSession.shaderManager.creatureShader);

    this._renderCreature(renderSession);

    renderSession.spriteBatch.start();
};

/**
 * @method Phaser.Creature#_renderCreature
 * @memberof Phaser.Creature
 * @private
 */
Phaser.Creature.prototype._renderCreature = function (renderSession)
{
    var gl = renderSession.gl;

    var projection = renderSession.projection;
    var offset = renderSession.offset;
    var shader = renderSession.shaderManager.creatureShader;

    renderSession.blendModeManager.setBlendMode(this.blendMode);

    //  Set uniforms
    gl.uniformMatrix3fv(shader.translationMatrix, false, this.worldTransform.toArray(true));
    gl.uniform2f(shader.projectionVector, projection.x, -projection.y);
    gl.uniform2f(shader.offsetVector, -offset.x, -offset.y);
    gl.uniform1f(shader.alpha, this.worldAlpha);
    gl.uniform3fv(shader.tintColor, Phaser.Color.hexToRGBArray(this.tint));
    gl.uniform1f(shader.alpha, this.alpha);

    if (!this.dirty)
    {
        gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
        gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);

        //  Update the uvs
        gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
        gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);

        // Update the colors
        gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW);

        gl.activeTexture(gl.TEXTURE0);

        //  Check if a texture is dirty..
        if (this.texture.baseTexture._dirty[gl.id])
        {
            renderSession.renderer.updateTexture(this.texture.baseTexture);
        }
        else
        {
            //  Bind the current texture
            gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]);
        }

        //  Don't need to upload!
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
    }
    else
    {
        this.dirty = false;

        gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
        gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);

        //  Update the uvs
        gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.DYNAMIC_DRAW);
        gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);

        // Update the colors
        gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW);
        gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false, 0, 0);

        gl.activeTexture(gl.TEXTURE0);

        //  Check if a texture is dirty
        if (this.texture.baseTexture._dirty[gl.id])
        {
            renderSession.renderer.updateTexture(this.texture.baseTexture);
        }
        else
        {
            gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]);
        }

        //  Don't need to upload!
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
    }

    gl.drawElements(gl.TRIANGLES, this.indices.length, gl.UNSIGNED_SHORT, 0);
};

/**
 * @method Phaser.Creature#updateCreatureBounds
 * @memberof Phaser.Creature
 * @private
 */
Phaser.Creature.prototype.updateCreatureBounds = function ()
{
    //  Update bounds based off world transform matrix
    var target = this.manager.target_creature;

    target.ComputeBoundaryMinMax();

    this.creatureBoundsMin.set(target.boundary_min[0], -target.boundary_min[1]);
    this.creatureBoundsMax.set(target.boundary_max[0], -target.boundary_max[1]);

    this.worldTransform.apply(this.creatureBoundsMin, this.creatureBoundsMin);
    this.worldTransform.apply(this.creatureBoundsMax, this.creatureBoundsMax);
};

/**
 * @method Phaser.Creature#updateData
 * @memberof Phaser.Creature
 * @private
 */
Phaser.Creature.prototype.updateData = function ()
{
    var target = this.manager.target_creature;

    var read_pts = target.render_pts;
    var read_uvs = target.global_uvs;

    this.updateRenderData(read_pts, read_uvs);
    this.updateCreatureBounds();

    this.dirty = true;
};

/**
 * @method Phaser.Creature#updateRenderData
 * @memberof Phaser.Creature
 * @private
 */
Phaser.Creature.prototype.updateRenderData = function (verts, uvs)
{
    var target = this.manager.target_creature;

    var pt_index = 0;
    var uv_index = 0;

    var write_pt_index = 0;

    for (var i = 0; i < target.total_num_pts; i++)
    {
        this.vertices[write_pt_index] = verts[pt_index];
        this.vertices[write_pt_index + 1] = -verts[pt_index + 1];

        this.uvs[uv_index] = uvs[uv_index];
        this.uvs[uv_index + 1] = uvs[uv_index + 1];

        pt_index += 3;
        uv_index += 2;

        write_pt_index += 2;
    }

    // Update color/opacity region values
    var render_composition =
        target.render_composition;
    var regions_map =
        render_composition.getRegionsMap();
    for(var region_name in regions_map)
    {
        var cur_region = regions_map[region_name];
        var start_pt_idx = cur_region.getStartPtIndex();
        var end_pt_idx = cur_region.getEndPtIndex() + 1;
        var cur_opacity = cur_region.opacity * 0.01;

        for(var i = (start_pt_idx * 4); i <= (end_pt_idx * 4); i++)
        {
            this.colors[i] = cur_opacity;
        }
    }
};

/**
 * Sets the Animation this Creature object will play, as defined in the mesh data.
 *
 * @method Phaser.Creature#setAnimation
 * @memberof Phaser.Creature
 * @param {string} key - The key of the animation to set, as defined in the mesh data.
 */
Phaser.Creature.prototype.setAnimation = function (key)
{
    this.data.anchorY = null;
    this.data.anchorX = null;
    this.data.animation = key;
    this.manager.SetActiveAnimationName(key, true);
};

/**
 * Sets the animation playback speed
 *
 * @method Phaser.Creature#setAnimationPlaySpeed
 * @memberof Phaser.Creature
 * @param {number} speed - Sets the playback speed
 */
Phaser.Creature.prototype.setAnimationPlaySpeed = function (speed)
{
    if (speed)
    {
        this.timeDelta = speed;
    }
};

/**
 * Plays the currently set animation.
 *
 * @method Phaser.Creature#play
 * @memberof Phaser.Creature
 * @param {boolean} [loop=false] - Should the animation loop?
 */
Phaser.Creature.prototype.play = function (loop)
{
    if (loop === undefined) { loop = false; }

    this.loop = loop;

    this.manager.SetIsPlaying(true);
    this.manager.RunAtTime(0);
};

/**
 * Stops the currently playing animation.
 *
 * @method Phaser.Creature#stop
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.stop = function ()
{
    this.manager.SetIsPlaying(false);
};

/**
 * @name Phaser.Creature#isPlaying
 * @property {boolean} isPlaying - Is the _current_ animation playing?
 */
Object.defineProperty(Phaser.Creature.prototype, 'isPlaying', {

    get: function ()
    {
        return this.manager.GetIsPlaying();
    },

    set: function (value)
    {
        this.manager.SetIsPlaying(value);
    }

});

/**
 * @name Phaser.Creature#loop
 * @property {boolean} loop - Should the _current_ animation loop or not?
 */
Object.defineProperty(Phaser.Creature.prototype, 'loop', {

    get: function ()
    {
        return this.manager.should_loop;
    },

    set: function (value)
    {
        this.manager.SetShouldLoop(value);
    }

});

/**
 * @name Phaser.Creature#height
 * @property {number} height - Sets the height of the animation
 */
Object.defineProperty(Phaser.Creature.prototype, 'height', {

    get: function ()
    {
        return this.data.height;
    },

    set: function (value)
    {
        var target = this.manager.target_creature;

        var width = this.data.width ? this.data.width : 0;

        var values = target.GetPixelScaling(width, value);
        this.scale.set(values[0], values[1]);
        this.data.height = value;
    }

});

/**
 * @name Phaser.Creature#width
 * @property {number} width - Sets the width of the animation
 */
Object.defineProperty(Phaser.Creature.prototype, 'width', {

    get: function ()
    {
        return this.data.width;
    },

    set: function (value)
    {
        var target = this.manager.target_creature;

        var height = this.data.height ? this.data.height : 0;

        var values = target.GetPixelScaling(value, height);
        this.scale.set(values[0], values[1]);
        this.data.width = value;
    }

});

/**
 * @name Phaser.Creature#anchorX
 * @property {number} anchorX - Sets the anchorX of the animation
 */
Object.defineProperty(Phaser.Creature.prototype, 'anchorX', {

    get: function ()
    {
        return this.data.anchorX;
    },

    set: function (value)
    {
        if (value === 0)
        {
            value = 0.01;
        }

        if (value === 1)
        {
            value = 0.99;
        }

        if (value === this.data.anchorX)
        {
            return;
        }

        var target = this.manager.target_creature;

        this.stop();
        this.manager.RunAtTime(0);

        if (this.data.anchorX)
        {
            target.SetAnchorPoint(-this.data.anchorX, null, this.data.animation);

            this.play(true);
            this.stop();
            this.manager.RunAtTime(0);
        }

        target.SetAnchorPoint(value, null, this.data.animation);
        this.play(true);

        this.data.anchorX = value;
    }

});

/**
 * @name Phaser.Creature#anchorY
 * @property {number} anchorY - Sets the anchorY of the animation
 */
Object.defineProperty(Phaser.Creature.prototype, 'anchorY', {

    get: function ()
    {
        return this.data.anchorY;
    },

    set: function (value)
    {
        if (value === 0)
        {
            value = 0.01;
        }

        if (value === 1)
        {
            value = 0.99;
        }

        if (value === this.data.anchorY)
        {
            return;
        }

        var target = this.manager.target_creature;

        this.stop();
        this.manager.RunAtTime(0);

        if (this.data.anchorY)
        {
            target.SetAnchorPoint(null, -this.data.anchorY, this.data.animation);

            this.play(true);
            this.stop();
            this.manager.RunAtTime(0);
        }

        target.SetAnchorPoint(null, value, this.data.animation);
        this.play(true);

        this.data.anchorY = value;
    }

});

/**
 * @name Phaser.Creature#tint
 * @property {number} tint - Sets the colour tint
 */
Object.defineProperty(Phaser.Creature.prototype, 'tint', {

    get: function ()
    {
        return this.data.tint;
    },

    set: function (value)
    {
        this.data.tint = value;
    }

});

/**
 * @name Phaser.Creature#alpha
 * @property {number} alpha - Sets the opacity
 */
Object.defineProperty(Phaser.Creature.prototype, 'alpha', {

    get: function ()
    {
        return this.data.alpha;
    },

    set: function (value)
    {
        this.data.alpha = value;
    }

});

/**
 * Sets whether anchor point transformations are active.
 *
 * @method Phaser.Creature#setAnchorPointEnabled
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.setAnchorPointEnabled = function (value)
{
    var target = this.manager.target_creature;
    target.SetAnchorPointEnabled(value);
};

/**
 * @method Phaser.Creature#createAllAnimations
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.createAllAnimations = function (mesh)
{
    if (!this.game.cache.checkJSONKey(mesh))
    {
        console.warn('Phaser.Creature: Invalid mesh key given. Not found in Phaser.Cache');
        return;
    }

    var meshData = this.game.cache.getJSON(mesh, true);

    this.manager.CreateAllAnimations(meshData);
};

/**
 * @method Phaser.Creature#setMetaData
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.setMetaData = function (meta)
{
    if (!this.game.cache.checkJSONKey(meta))
    {
        console.warn('Phaser.Creature: Invalid meta key given. Not found in Phaser.Cache');
        return;
    }

    var metaJson = this.game.cache.getJSON(meta, true);
    var metaData = CreatureModuleUtils.BuildCreatureMetaData(metaJson);

    this._creature.SetMetaData(metaData);
};

/**
 * @method Phaser.Creature#enableSkinSwap
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.enableSkinSwap = function (swapNameIn, active)
{
    var target = this.manager.target_creature;

    if (target.creature_meta_data === null)
    {
        console.warn('Phaser.Creature: Attempting to use skin swapping before setting the meta data. You must use {@link #setMetaData} before using skin swapping functionality.');
        return;
    }

    target.EnableSkinSwap(swapNameIn, active);

    this.indices = new Uint16Array(target.final_skin_swap_indices.length);
    for(var i = 0; i < this.indices.length; i++)
    {
        this.indices[i] = target.final_skin_swap_indices[i];
    }
};

/**
 * @method Phaser.Creature#disableSkinSwap
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.disableSkinSwap = function ()
{
    var target = this.manager.target_creature;

    if (target.creature_meta_data === null)
    {
        console.warn('Phaser.Creature: Attempting to use skin swapping before setting the meta data. You must use {@link #setMetaData} before using skin swapping functionality.');
        return;
    }

    target.DisableSkinSwap();

    this.indices = new Uint16Array(target.global_indices.length);
    for(var i = 0; i < this.indices.length; i++)
    {
        this.indices[i] = target.global_indices[i];
    }
};

/**
 * @method Phaser.Creature#setActiveItemSwap
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.setActiveItemSwap = function (regionName, swapIdx)
{
    var target = this.manager.target_creature;

    target.active_uv_swap_actions[regionName] = swapIdx;
};

/**
 * @method Phaser.Creature#removeActiveItemSwap
 * @memberof Phaser.Creature
 */
Phaser.Creature.prototype.removeActiveItemSwap = function (regionName)
{
    var target = this.manager.target_creature;

    delete target.active_uv_swap_actions[regionName];
};
